/*
 * INCLUDE FILES
 ****************************************************************************************
 */
#include <stdio.h>
#include <string.h>

#include "gap_api.h"
#include "gatt_api.h"
#include "ble_stack.h"

#include "os_timer.h"
#include "os_mem.h"
#include "button.h"
#include "jump_table.h"
#include "co_printf.h"
#include "co_log.h"
#include "sys_utils.h"

#include "user_task.h"
#include "app_at.h"

#include "plf.h"
#include "driver_system.h"
#include "driver_pmu.h"
#include "driver_uart.h"
#include "flash_usage_config.h"

#include "ble_simple_central.h"
#include "simple_gatt_service.h"
#include "co_log.h"

#undef LOG_LOCAL_LEVEL
#define LOG_LOCAL_LEVEL        (LOG_LEVEL_INFO)
uint8_t *app_tag = "project";

#define SYSTEM_STACK_SIZE           0x800

/*
 * LOCAL VARIABLES
 */

__attribute__((section("stack_section"))) static uint32_t system_stack[SYSTEM_STACK_SIZE/sizeof(uint32_t)];

const struct jump_table_version_t _jump_table_version __attribute__((section("jump_table_3"))) = 
{
    .stack_top_address = &system_stack[SYSTEM_STACK_SIZE/sizeof(uint32_t)],
    .firmware_version = 0x00000000,
};

const struct jump_table_image_t _jump_table_image __attribute__((section("jump_table_1"))) =
{
    .image_type = IMAGE_TYPE_APP,
    .image_size = 0x19000,      
};


__attribute__((section("ram_code"))) void pmu_gpio_isr_ram(void)
{
    uint32_t gpio_value = ool_read32(PMU_REG_PORTA_V);
    
    button_toggle_detected(gpio_value);
    ool_write32(PMU_REG_PORTA_LAST_STATUS, gpio_value);
}



/*********************************************************************
 * @fn      user_entry_before_sleep_imp
 *
 * @brief   Before system goes to sleep mode, user_entry_before_sleep_imp()
 *          will be called, MCU peripherals can be configured properly before 
 *          system goes to sleep, for example, some MCU peripherals need to be
 *          used during the system is in sleep mode. 
 *
 * @param   None. 
 *       
 *
 * @return  None.
 */
__attribute__((section("ram_code"))) void user_entry_before_sleep_imp(void)
{
	uart_putc_noint_no_wait(UART0, 's');
}

/*********************************************************************
 * @fn      user_entry_after_sleep_imp
 *
 * @brief   After system wakes up from sleep mode, user_entry_after_sleep_imp()
 *          will be called, MCU peripherals need to be initialized again, 
 *          this can be done in user_entry_after_sleep_imp(). MCU peripherals
 *          status will not be kept during the sleep. 
 *
 * @param   None. 
 *       
 *
 * @return  None.
 */
__attribute__((section("ram_code"))) void user_entry_after_sleep_imp(void)
{
	system_set_port_mux(GPIO_PORT_A, GPIO_BIT_0, PORTA0_FUNC_UART0_RXD);
    system_set_port_mux(GPIO_PORT_A, GPIO_BIT_1, PORTA1_FUNC_UART0_TXD);
    uart_init(UART0, 1152);
    fr_uart_enableIrq(UART0, Uart_irq_erbfi);
	
	uart_putc_noint_no_wait(UART0, 'w');
	NVIC_EnableIRQ(PMU_IRQn);	
}

__attribute__((section("ram_code"))) void main_loop(void)
{
    bool init_rf = false;
    extern uint8_t wakeup_end;
    wakeup_end = 0;	
    while(1)
    {
        ble_stack_schedule();
        GLOBAL_INT_DISABLE();
        switch(ble_stack_sleep_check())
        {
            case 2:
            {
                ool_write(PMU_REG_SYSTEM_STATUS_SW, PMU_SYS_WK_MAGIC);
                user_entry_before_sleep_imp();
                low_power_save();
                low_power_restore();
                init_rf = true;
                ool_write(PMU_REG_SYSTEM_STATUS_SW, PMU_SYS_PO_MAGIC);
            }
            break;
            default:
                break;
        }
        GLOBAL_INT_RESTORE();
				
		if(wakeup_end)
        {
            wakeup_end = 0;
            user_entry_after_sleep_imp();
        }
        if(init_rf)
        {
            init_rf = false;
            ble_stack_rf_init();
        }
    }
}

/*********************************************************************
 * @fn      proj_init
 *
 * @brief   Main entrancy of user application. This function is called after BLE stack
 *          is initialized, and all the application code will be executed from here.
 *          In that case, application layer initializtion can be startd here. 
 *
 * @param   None. 
 *       
 *
 * @return  None.
 */
void proj_init(void)
{
    co_printf("BLE Central\r\n");
    
    // User task initialization, for buttons.
    user_task_init();
    
    app_at_init();
    
    // Application layer initialization, can included bond manager init, 
    // advertising parameters init, scanning parameter init, GATT service adding, etc.    
    simple_central_init();
}

/*********************************************************************
 * @fn      user_entry_before_ble_init
 *
 * @brief   Code to be executed before BLE stack to be initialized.
 *          Power mode configurations, PMU part driver interrupt enable, MCU 
 *          peripherals init, etc. 
 *
 * @param   None. 
 *       
 *
 * @return  None.
 */
void user_main(void)
{    
    /* initialize PMU module at the beginning of this program */
    pmu_sub_init();

    /* set log output levels used in host library */
    log_enable = true;
    log_with_file = false;
    tim_local_log_level = LOG_LEVEL_NONE;
    gap_local_log_level = LOG_LEVEL_INFO;
    sm_local_log_level = LOG_LEVEL_INFO;
    bond_m_local_log_level = LOG_LEVEL_INFO;
    gatt_local_log_level= LOG_LEVEL_NONE;

    /* intialize serveral parameters in __jump_table */
    memcpy(&__jump_table.addr.addr[0],"\xBD\xAD\x10\x11\x20\x20",6);
    ble_set_addr_type(BLE_ADDR_TYPE_PUBLIC);
    __jump_table.system_clk = SYSTEM_SYS_CLK_48M;
    __jump_table.system_option &= (~SYSTEM_OPTION_CODED_PHY_500);
    __jump_table.system_option &= (~SYSTEM_OPTION_SLEEP_ENABLE);
    __jump_table.sleep_algo_dur = 4;
    __jump_table.twext = 3000;
    __jump_table.twosc = 3000;
    jump_table_set_static_keys_store_offset(JUMP_TABLE_STATIC_KEY_OFFSET);

    /* initialize system clock, etc. */
    system_set_pclk(__jump_table.system_clk);
    system_regs->misc.ble_phase_match_sel = 1;
    SCB->VTOR = 0x11000000; //set exception vector offset to RAM space
    co_delay_100us(1);

    void intc_init(void);
    intc_init();

    /* calibrate internal RC clock */
    pmu_calibration_start(PMU_CALI_SEL_RCLFOSC, __jump_table.lp_clk_calib_cnt);
    co_delay_10us(__jump_table.lp_clk_calib_cnt * 3);
    pmu_get_rc_clk(true);
    pmu_calibration_stop();

    /* initialize ble stack */
    ble_stack_configure(false, 1, 8, 100, 8, 100, 31);
    ble_stack_init();

    proj_init();

    GLOBAL_INT_START();

    /* enter main loop */
    main_loop();
}
